package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/imp/enums"
	"bgs/service/model"
	"bgs/util"
	"bgs/wxp/mina"
	"context"
	"encoding/json"
	"fmt"
	"time"
)

// GenerateAfterSaleNumber 单号生成器
func (s *AfterSaleServiceImp) GenerateAfterSaleNumber(afterSaleTypeInt int32, userID int64) string {
	// 'T'+保留(5)+售后类型(1)+用户id(10)+(秒时间戳14)+随机数(3) = 32
	return fmt.Sprintf("T%05d%d%s%s%03d",
		0,
		afterSaleTypeInt, // 1: refund 2: refund_and_return_goods
		util.PaddingLeftZero(userID, 10),
		util.GoTimeToStringf(time.Now(), "060102150405"),
		util.RandomInt(0, 999))
}

// CreateAfterSaleDetailItem 创建售后单明细参数
type CreateAfterSaleDetailItem struct {
	OrderDetailID        int64
	Quantity             int32
	GoodsReceivingStatus enums.EnumGoodsReceivingStatus
}

// CreateAfterSaleParam 用户创建售后单参数
type CreateAfterSaleParam struct {
	OrderPtr        *sqlc.Order
	UserID          int64
	Type            enums.EnumAfterSaleType
	Remark          string
	AfterSaleReason string
	Details         []*CreateAfterSaleDetailItem
}

// CreateAfterSale 用户创建售后单(一次售后的可正对多个商品,但是必须都是同一个类型的售后,要么退款要么退货退款,且必须是来自同一个仓库)
func (s *AfterSaleServiceImp) CreateAfterSale(ctx context.Context, q *sqlc.Queries, p CreateAfterSaleParam) (afterSale sqlc.AfterSale, afterSaleDetails []sqlc.AfterSaleDetail, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	if _, found := util.StringFind([]string{
		string(enums.OrderStatusPayed),
		string(enums.OrderStatusDelivered),
		string(enums.OrderStatusPrepared),
		string(enums.OrderStatusFinished),
	}, p.OrderPtr.OrderStatus); !found {
		err = fmt.Errorf("无法售后,订单状态:%s", p.OrderPtr.OrderStatus)
		return
	}

	orderDetails, err := q.ListNormalOrderDetailsByOrderID(ctx, p.OrderPtr.ID)
	if err != nil {
		return
	}
	mapOfOrderDetails := util.SliceToMapInt64(util.ToGenericSlice(orderDetails), func(m map[int64]interface{}, v interface{}) {
		val := v.(sqlc.OrderDetail)
		m[val.ID] = v
	})
	slog.Infof("2 获取要售后的订单明细 ok")

	var upsertAfterSaleDetails []sqlc.UpsertAfterSaleDetailParams
	var amountOfDetails int64
	for _, item := range p.Details {
		if v, found := mapOfOrderDetails[item.OrderDetailID]; found {
			val := v.(sqlc.OrderDetail)
			// 检测可退数量
			availableQuantity := val.Quantity - val.AfterSaleQuantity.Int32 - val.AfterSaleQuantityUndetermined.Int32
			slog.Infof("val.Quantity:%d - val.AfterSaleQuantity:%d - val.AfterSaleQuantityUndetermined:%d", val.Quantity, val.AfterSaleQuantity.Int32, val.AfterSaleQuantityUndetermined.Int32)
			if item.Quantity > availableQuantity {
				err = fmt.Errorf("售后商品明细[%d]数量错误,可退数量为:%d", item.OrderDetailID, availableQuantity)
				return
			}
			amountOfEachItem := int64(item.Quantity * val.Price)
			upsertAfterSaleDetails = append(upsertAfterSaleDetails, sqlc.UpsertAfterSaleDetailParams{
				OrderID:              p.OrderPtr.ID,
				OrderSerialNumber:    p.OrderPtr.SerialNumber,
				ProductID:            val.ProductID,
				ProductSpecID:        val.ProductSpecID,
				ProductSpecKey:       val.ProductSpecKey,
				Quantity:             item.Quantity,
				GoodsReceivingStatus: string(item.GoodsReceivingStatus),
				Price:                val.Price,
				Amount:               amountOfEachItem,
			})
			amountOfDetails += amountOfEachItem
		} else {
			err = fmt.Errorf("售后商品明细[%d]不在订单明细中", item.OrderDetailID)
			return
		}
	}
	slog.Infof("3 检测可退数量 ok")

	isRefund := p.Type == enums.AfterSaleTypeRefund
	isWholeRefund := p.OrderPtr.OrderStatus == string(sqlc.EmOrderStatusPayed) // 已支付(待发货/待备货的售后就是整单退)

	// 未发货/未备货运费也需要退, 已发货/已备货运费不用退
	refundAmountOfPayed := util.Int64IfThenElse(p.OrderPtr.OrderStatus == string(sqlc.EmOrderStatusPayed), p.OrderPtr.ActualAmount, p.OrderPtr.ActualAmount-int64(p.OrderPtr.ShippingFee))
	refundAmountOfAfterPayed := amountOfDetails
	amount := util.Int64IfThenElse(isWholeRefund, refundAmountOfPayed, refundAmountOfAfterPayed)

	afterSale, err = q.CreateAfterSale(ctx, sqlc.CreateAfterSaleParams{
		SerialNumber:      s.GenerateAfterSaleNumber(util.Int32IfThenElse(isRefund, 1, 2), p.UserID),
		BrandID:           p.OrderPtr.BrandID,
		StoreID:           p.OrderPtr.StoreID,
		DistributorID:     p.OrderPtr.DistributorID,
		DistributorUserID: p.OrderPtr.DistributorUserID,
		ActivityID:        p.OrderPtr.ActivityID,
		RootActivityID:    p.OrderPtr.RootActivityID,
		ParentActivityID:  p.OrderPtr.ParentActivityID,
		OrderID:           p.OrderPtr.ID,
		OrderSerialNumber: p.OrderPtr.SerialNumber,
		UserID:            p.UserID,
		SpokesmanID:       p.OrderPtr.SpokesmanID,
		SpokesmanUserID:   p.OrderPtr.SpokesmanUserID,
		Type:              string(p.Type),
		Amount:            amount,
		Remark:            sqlc.NSFromString(p.Remark),
		AfterSaleReason:   p.AfterSaleReason,
		IsWholeRefund:     isWholeRefund,
	})
	if err != nil {
		return
	}
	slog.Infof("4 创建售后单 ok")

	var afterSaleDetail sqlc.AfterSaleDetail
	for _, item := range upsertAfterSaleDetails {
		item.AfterSaleID = afterSale.ID
		item.AfterSaleSerialNumber = afterSale.SerialNumber
		afterSaleDetail, err = q.UpsertAfterSaleDetail(ctx, item)
		if err != nil {
			return
		}
		afterSaleDetails = append(afterSaleDetails, afterSaleDetail)
	}
	slog.Infof("5 创建售后单明细 ok")

	for _, item := range p.Details {
		_, err = q.UpdateOrderDetailAfterSaleSerialNumberWithQuantityUndetermined(ctx,
			sqlc.UpdateOrderDetailAfterSaleSerialNumberWithQuantityUndeterminedParams{
				ID:                            item.OrderDetailID,
				AfterSaleSerialNumber:         sqlc.NSFromString(afterSale.SerialNumber),
				AfterSaleQuantityUndetermined: sqlc.NInt32FromInt32(item.Quantity),
			})
		if err != nil {
			return
		}
	}
	slog.Infof("6 更新订单明细售后单号及售后中的数量 ok")
	return
}

// CancelAfterSale 取消售后单
func (s *AfterSaleServiceImp) CancelAfterSale(ctx context.Context, q *sqlc.Queries, afterSaleID int64) (afterSale sqlc.AfterSale, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSale, err = q.CancelAfterSale(ctx, afterSaleID)
	slog.Infof("1 取消售后单 ok")
	return
}

// UpdateAfterSaleWxRefundIDParam 商家更新售后的微信退款单号
type UpdateAfterSaleWxRefundIDParam struct {
	AfterSaleID int64
	WxRefundID  string
}

// UpdateAfterSaleWxRefundID 更新微信的退款单号
func (s *AfterSaleServiceImp) UpdateAfterSaleWxRefundID(ctx context.Context, q *sqlc.Queries, p UpdateAfterSaleWxRefundIDParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	_, err = q.UpdateAfterSaleWxRefundID(ctx, sqlc.UpdateAfterSaleWxRefundIDParams{
		ID:         p.AfterSaleID,
		WxRefundID: sqlc.NSFromString(p.WxRefundID),
	})
	if err != nil {
		return
	}
	slog.Infof("1 更新微信的退款单号 ok")
	return
}

// RefuseAfterSaleDetails 拒绝的售后单明细
type RefuseAfterSaleDetails struct {
	AfterSaleDetailID     int64
	RefuseAfterSaleReason string
}

// AuditAfterSaleParam 商家审核售后单参数
type AuditAfterSaleParam struct {
	AfterSalePtr *sqlc.AfterSale
	OrderPtr     *sqlc.Order
	IsRefuse     bool
	RefuseReason string
}

// AuditAfterSale 商家审核售后单
func (s *AfterSaleServiceImp) AuditAfterSale(ctx context.Context, q *sqlc.Queries, p AuditAfterSaleParam) (afterSale sqlc.AfterSale, needSendNotify bool, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSaleID := p.AfterSalePtr.ID

	var isOnlyRefund bool

	if p.AfterSalePtr.IsWholeRefund {
		isOnlyRefund = true
		slog.Infof("1 标记整单售后退款 ok")

	} else {
		isOnlyRefund = p.AfterSalePtr.Type == string(enums.AfterSaleTypeRefund)

		slog.Infof("1 标记售后部分退款/退货 ok")
	}

	if p.IsRefuse {
		afterSale, err = q.AuditToRefuseAfterSale(ctx, sqlc.AuditToRefuseAfterSaleParams{
			ID:                    afterSaleID,
			RefuseAfterSaleReason: sqlc.NSFromString(p.RefuseReason),
		})
		if err != nil {
			return
		}
		slog.Infof("2 商家拒绝售后,售后关闭 ok")
		needSendNotify = true
	} else {
		if isOnlyRefund {
			afterSale, err = q.AuditAfterSaleForRefund(ctx, afterSaleID)
			if err != nil {
				return
			}
			slog.Infof("2 审核同意售后退款 ok")
		} else {

			// 读取订单的发货仓库地址信息
			orderExtInfo := &model.OrderExtInfo{}
			err = json.Unmarshal(p.OrderPtr.ExtInfo, orderExtInfo)
			if err != nil {
				return
			}

			returnGoodsAddressInfoPtr := &model.AddressInfo{
				Address: orderExtInfo.WarehouseInfo.Address,
				Linkman: orderExtInfo.WarehouseInfo.Linkman,
				Tel:     orderExtInfo.WarehouseInfo.Tel,
			}

			returnGoodsAddressInfo, marshalErr := json.Marshal(returnGoodsAddressInfoPtr)
			if marshalErr != nil {
				err = marshalErr
				return
			}

			afterSale, err = q.AuditAfterSaleForRefundAndReturnGoods(ctx, sqlc.AuditAfterSaleForRefundAndReturnGoodsParams{
				ID:                     afterSaleID,
				ReturnGoodsAddressInfo: sqlc.NRawMessageFromRawMessage(json.RawMessage(returnGoodsAddressInfo)),
			})
			if err != nil {
				return
			}
			slog.Infof("2 审核同意售后退款退货 ok")
			needSendNotify = true
		}
	}

	return
}

// SendAfterSaleReturnGoodsParam 用户寄出退货参数
type SendAfterSaleReturnGoodsParam struct {
	AfterSalePtr *sqlc.AfterSale
	PlatformID   uint32
	PlatformName string
	PlatformNo   string
}

// SendAfterSaleReturnGoods 用户寄出退货物品
func (s *AfterSaleServiceImp) SendAfterSaleReturnGoods(ctx context.Context, q *sqlc.Queries, p SendAfterSaleReturnGoodsParam) (afterSale sqlc.AfterSale, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	returnGoodsLogisticsInfo := model.LogisticsInfoEx{
		ID:   p.PlatformID,
		Name: p.PlatformName,
		No:   p.PlatformNo,
	}
	bytesOfReturnGoodsLogisticsInfo, err := json.Marshal(returnGoodsLogisticsInfo)
	if err != nil {
		return
	}
	afterSale, err = q.UpdateAfterSaleSendReturnGoods(ctx, sqlc.UpdateAfterSaleSendReturnGoodsParams{
		ID:                       p.AfterSalePtr.ID,
		ReturnGoodsLogisticsInfo: sqlc.NRawMessageFromRawMessage(json.RawMessage(bytesOfReturnGoodsLogisticsInfo)),
	})
	slog.Infof("1 用户寄出退货 ok")
	return
}

// ReceiveAfterSaleReturnGoods 商家入库退货物品
func (s *AfterSaleServiceImp) ReceiveAfterSaleReturnGoods(ctx context.Context, q *sqlc.Queries, afterSaleID int64) (afterSale sqlc.AfterSale, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSale, err = q.UpdateAfterSaleReceiveReturnGoods(ctx, afterSaleID)
	slog.Infof("1 商家入库退货物品 ok")
	return
}

// RefuseReceiveAfterSaleReturnGoodsParam 商家拒绝入库退货物品参数
type RefuseReceiveAfterSaleReturnGoodsParam struct {
	AfterSaleID           int64
	RefuseAfterSaleReason string
}

// RefuseReceiveAfterSaleReturnGoods 商家拒绝入库退货物品
func (s *AfterSaleServiceImp) RefuseReceiveAfterSaleReturnGoods(ctx context.Context, q *sqlc.Queries, p RefuseReceiveAfterSaleReturnGoodsParam) (afterSale sqlc.AfterSale, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSale, err = q.RefuseReceiveAfterSaleReturnGoods(ctx, sqlc.RefuseReceiveAfterSaleReturnGoodsParams{
		ID:                    p.AfterSaleID,
		RefuseAfterSaleReason: sqlc.NSFromString(p.RefuseAfterSaleReason),
	})
	slog.Infof("1 售后退货商家拒绝入库 ok")
	return
}

// RefundAfterSaleParam 发起售后退款参数
type RefundAfterSaleParam struct {
	OrderPtr     *sqlc.Order
	AfterSalePtr *sqlc.AfterSale
}

// RefundAfterSale 发起售后退款
func (s *AfterSaleServiceImp) RefundAfterSale(ctx context.Context, q *sqlc.Queries, p RefundAfterSaleParam) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	var remainRefundAmount int64
	// 未发货/未备货运费也需要退, 已发货/已备货运费不用退
	if p.OrderPtr.OrderStatus == string(sqlc.EmOrderStatusPayed) {
		remainRefundAmount = p.OrderPtr.ActualAmount + int64(p.OrderPtr.ShippingFee) - p.OrderPtr.RefundAmount
	} else {
		remainRefundAmount = p.OrderPtr.ActualAmount - p.OrderPtr.RefundAmount
	}

	toRefundAmount := p.AfterSalePtr.Amount

	if remainRefundAmount < toRefundAmount {
		err = fmt.Errorf("可退金额不能小于售后退款金额")
		return
	}

	_, err = q.RefundAfterSale(ctx, p.AfterSalePtr.ID)
	if err != nil {
		return
	}
	slog.Infof("2 更新售后单状态为退款中 ok")

	return
}

// MarkOrderCounted 标记订单已统计 (主动)
func (s *AfterSaleServiceImp) MarkCounted(ctx context.Context, q *sqlc.Queries, afterSaleID int64) (afterSale sqlc.AfterSale, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSale, err = q.MarkAfterSaleCounted(ctx, afterSaleID)
	if err != nil {
		return
	}
	slog.Info("1、标记售后单已统计 ok")

	return
}

// MarkAccounted 标记售后单已记账 (主动)
func (s *AfterSaleServiceImp) MarkAccounted(ctx context.Context, q *sqlc.Queries, afterSaleID int64) (afterSale sqlc.AfterSale, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSale, err = q.MarkAfterSaleAccounted(ctx, afterSaleID)
	if err != nil {
		return
	}
	slog.Info("1、标记售后单已记账 ok")

	return
}

// CloseAfterSaleForRefundFail 因为退款或回退奖励金失败而关闭售后单
func (s *AfterSaleServiceImp) CloseAfterSaleForRefundFail(ctx context.Context, q *sqlc.Queries, afterSaleSerialNumber string) (err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	_, err = q.CloseAfterSaleForRefundFailBySerialNumber(ctx, afterSaleSerialNumber)
	if err != nil {
		return
	}
	slog.Info("1、因为退款失败关闭售后单 ok")

	return
}

type ProductNameMap map[string]string

// GenOrderDeliveredNotifyMessage (主动,生成微信小程序消息=>售后单状态变化)
func (s *AfterSaleServiceImp) GenAfterSaleStatusChangedNotifyMessage(ctx context.Context, q *sqlc.Queries, afterSaleSerialNumber string) (ret mina.SendNotifyParam, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	afterSale, err := q.GetAfterSaleBySerialNumber(ctx, afterSaleSerialNumber)
	if err != nil {
		return
	}
	slog.Info("1、读取售后单 ok")

	var afterSaleStatusName string
	switch sqlc.EmAfterSaleStatus(afterSale.AfterSaleStatus) {
	case sqlc.EmAfterSaleStatusFinished:
		afterSaleStatusName = "退款已完成"
	case sqlc.EmAfterSaleStatusClosed:
		afterSaleStatusName = "申请被拒"
	case sqlc.EmAfterSaleStatusWaitSend:
		afterSaleStatusName = "请尽快退货"
	}

	var afterSaleTypeName string
	switch sqlc.EmAfterSaleType(afterSale.Type) {
	case sqlc.EmAfterSaleTypeRefund:
		afterSaleTypeName = "仅退款"
	case sqlc.EmAfterSaleTypeRefundAndReturnGoods:
		afterSaleTypeName = "退款退货"
	}

	afterSaleDetails, err := q.ListNormalAfterSaleDetailsByAfterSaleID(ctx, afterSale.ID)
	if err != nil {
		return
	}
	slog.Info("2、读取售后单明细 ok")

	orderSerialNumber := afterSale.OrderSerialNumber
	order, err := q.GetOrder(ctx, afterSale.OrderID)
	if err != nil {
		return
	}
	slog.Info("2、读取订单 ok")
	orderExtInfo := &model.OrderExtInfo{}
	err = json.Unmarshal(order.ExtInfo, orderExtInfo)
	if err != nil {
		err = fmt.Errorf("unmarshal order ext_info失败:%s", err.Error())
		return
	}
	slog.Info("3、unmarshal order ext_info ok")
	productNameMap := make(ProductNameMap)

	util.Each(util.ToGenericSlice(orderExtInfo.Details), func(v interface{}) {
		val := v.(*model.OrderDetailInExtInfo)
		k := val.ProductSpecKey
		productNameMap[k] = val.ProductName
	})

	afterSaleProductNamesDesc := util.JoinString(util.MapToString(util.ToGenericSlice(afterSaleDetails), func(v interface{}) string {
		val := v.(sqlc.AfterSaleDetail)
		k := val.ProductSpecKey
		return fmt.Sprintf("%sx%d", productNameMap[k], val.Quantity)
	}), ";")
	slog.Info("4、afterSaleProductNamesDesc ok")
	ret.Page = fmt.Sprintf("package-order/pages/after-sale-detail/index?orderNumber=%s", afterSaleSerialNumber)
	ret.TemplateID = s.wechatVars.Tp5334 //"KSpNahVXhdF6PoX1X2d8xXrQj34neGob4xEXdJpibA0"
	ret.UserOpenID = order.UserOpenID
	ret.MinaState = s.wechatVars.MinaState
	afterSaleAmountForNotify := fmt.Sprintf("%.2f", float64(afterSale.Amount)/100)
	ret.Content = map[string]map[string]interface{}{
		"character_string9": {"value": fmt.Sprintf("****%s", util.RightString(orderSerialNumber, 4))},
		"phrase8":           {"value": afterSaleTypeName},
		"thing7":            {"value": afterSaleProductNamesDesc},
		"amount5":           {"value": afterSaleAmountForNotify},
		"phrase1":           {"value": afterSaleStatusName},
	}

	slog.Infof(">>生成微信小程序消息=>售后单状态变化 ok")

	return
}
